<template>
  <div class="chat-app">
    <!-- 左侧好友列表 -->
    <FriendsList
      :friends="friends"
      :selectedFriendId="selectedFriendId"
      @selectFriend="handleSelectFriend"
    />

    <section class="chat-section">
      <template v-if="!activeFriend">
        <div class="default-page">
          <h2>欢迎来到小圈</h2>
          <p>选择一位好友开始聊天吧</p>
        </div>
      </template>
      <template v-else>
        <div class="chat-header">
          <!--    头像      -->
          <img :src="activeFriend.avatar" alt="avatar" class="avatar" />
          <!--    昵称     -->
          <div class="nickname">{{ activeFriend.memberId }}</div>
          <!--     在线状态     -->
          <div class="status">
            <div class="point" />
            在线
          </div>
          <!--     音视频通话     -->
          <div class="call" @click="handleStartVideoCall">
            <img class="call-img" :src="Video" alt="" />
          </div>
        </div>
        <div ref="chatBody" class="chat-body" @scroll="handleScroll">
          <div v-if="noMoreMessages" class="no-more">没有更多消息啦</div>
          <div v-if="loading" v-loading class="loading" />
          <div
            v-if="canLoadMore"
            class="load-more"
            @click="handleLoadMoreMessages"
          >
            加载更多
          </div>
          <div
            v-for="message in messages"
            :key="message.messageId"
            class="message"
            :data-id="message.messageId"
          >
            {{ handleMessageRead(message) }}
            <div
              :class="['message-text', isMe(message.fromUid) ? 'me' : 'friend']"
            >
              {{ message.content }}
              <div v-if="isShowUnreadDot(message)" class="unread-dot" />
            </div>
            <div class="message-timestamp">
              {{ formatMessageTimestamp(message.sendTime) }}
            </div>
          </div>
        </div>

        <!--   聊天输入框    -->
        <ChatInput :friend="activeFriend" @send-message="sendMessage" />
      </template>
    </section>

    <RightPanel @add-friend="showAddFriendDialog = true" />
    <el-dialog v-model="showAddFriendDialog" title="添加好友">
      <el-input v-model="newFriendId" placeholder="输入好友 ID" />
      <template #footer>
        <el-button @click="showAddFriendDialog = false">取消</el-button>
        <el-button type="primary" @click="addFriend">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { onMounted, ref, nextTick, watch, computed } from "vue";
import wsClient from "@/ws/wsClient";
import { addRecentFriend, getRecentInfo, recentList } from "@/api/contact";
import RightPanel from "./right-panel.vue";
import FriendsList from "./friends-list.vue";
import { useUserStoreHook } from "@/store/modules/user";
import { uuid } from "@pureadmin/utils";
import ChatInput from "@/views/chat/chat-input.vue";
import { ipcRenderer } from "electron";

import Video from "@/assets/video/video.png";
import { loadMessages } from "@/api/message";

import newMessage from "@/assets/audio/new-message.wav";
const newMessageAudio = new Audio(newMessage);

// 好友列表
const friends = ref([]);
const selectedFriendId = ref(null);

const activeFriend = ref(null);

const messages = computed(() => activeFriend.value?.last10Messages || []);

const newFriendId = ref("");
const showAddFriendDialog = ref(false);
const chatBody = ref(null);

// 加载状态
const loading = ref(false);

// 没有更多消息了
const noMoreMessages = ref(false);

// 是否为自己发送的消息
const isMe = sender => sender === useUserStoreHook().username;

// 是否展示未读红点
const isShowUnreadDot = message => {
  return message.isRead === false && isMe(message.fromUid);
};

// 格式化消息发送时间
const formatMessageTimestamp = timestamp => {
  const date = new Date(timestamp);
  const hours = date.getHours().toString().padStart(2, "0");
  const minutes = date.getMinutes().toString().padStart(2, "0");
  return `${hours}:${minutes}`;
};

const sendMessage = async newMessage => {
  const tempMessageId = uuid();
  if (newMessage !== "" && newMessage !== null && newMessage !== undefined) {
    activeFriend.value.last10Messages.push({
      tempMessageId: tempMessageId,
      fromUid: useUserStoreHook().username,
      content: newMessage,
      sendTime: Date.now()
    });
    // 好友列表顺序调整
    friends.value = friends.value.filter(
      friend => friend.memberId !== activeFriend.value.memberId
    );
    friends.value.unshift(activeFriend.value);
    await nextTick(() => {
      scrollToBottom();
    });
    const resp = await wsClient.sendMessage({
      action: "chat.direct.text.msg",
      data: {
        toUid: activeFriend.value.memberId,
        content: newMessage
      }
    });
    console.log("Message sent: ", resp);
    const { code, data } = resp;
    if (code !== 0) {
      // 消息发送失败
      // TODO: 处理消息发送失败的情况
    } else {
      // 消息发送成功
      const messageIndex = activeFriend.value.last10Messages.findIndex(
        message => message.tempMessageId === tempMessageId
      );
      activeFriend.value.last10Messages[messageIndex] = {
        ...data
      };
    }
  }
};

const scrollToBottom = () => {
  if (chatBody.value) {
    chatBody.value.scrollTop = chatBody.value.scrollHeight;
  }
};

// 消息到达事件
const onMessageArrived = () => {
  wsClient.registerActionHandler("chat.direct.text.msg", async message => {
    console.log("Received message: ", message);
    const { data } = message;
    const { fromUid } = data;
    // 找到对应的好友
    let friend = friends.value.find(friend => friend.memberId === fromUid);
    if (!friend) {
      // 好友列表不存在好友，调用接口获取好友列表
      const { data } = await getRecentInfo({
        targetId: fromUid,
        targetType: 0
      });
      friends.value.push(data);
      friend = data;
    }
    friend.last10Messages.push({
      ...data
    });
    friend.unreadCount++;
    // 好友列表顺序调整
    friends.value = friends.value.filter(f => f.memberId !== fromUid);
    friends.value.unshift(friend);

    if (activeFriend.value && activeFriend.value.memberId === fromUid) {
      await nextTick(() => {
        scrollToBottom();
      });
    } else {
      await newMessageAudio.play();
    }
  });
};

// 消息已读事件
const onMessageRead = () => {
  wsClient.registerActionHandler("chat.direct.text.msg.read", message => {
    console.log("Received message read: ", message);
    const { data } = message;
    const messages = activeFriend.value.last10Messages;
    const messageIndex = messages.findIndex(
      message => message.messageId === data.messageId
    );
    if (messageIndex !== -1) {
      messages[messageIndex].isRead = true;
    }
  });
};

// 处理消息已读
const handleMessageRead = message => {
  if (message.isRead) {
    return;
  }
  message.isRead = true;
  if (isMe(message.fromUid)) {
    return;
  }
  activeFriend.value.unreadCount--;
  publishMessageRead(message.messageId);
};

// 发布消息已读事件
const publishMessageRead = async messageId => {
  // 更新消息状态为已读
  messages.value.find(message => message.messageId === messageId).isRead = true;
  const resp = await wsClient.sendMessage({
    action: "chat.direct.text.msg.read",
    data: {
      messageId: messageId
    }
  });
  console.log("Message read sent: ", resp);
};

const handleSelectFriend = friend => {
  reset();
  activeFriend.value = friend;
  selectedFriendId.value = friend.memberId;
  nextTick(() => {
    scrollToBottom();
  });
};

// 重置聊天状态
const reset = () => {
  activeFriend.value = null;
  selectedFriendId.value = null;
  scrollToTop.value = -1;
  loading.value = false;
  noMoreMessages.value = false;
};

// 添加好友功能
const addFriend = async () => {
  if (newFriendId.value.trim()) {
    const friendId = newFriendId.value.trim();
    const params = {
      targetId: friendId,
      targetType: 0
    };
    const resp = await addRecentFriend(params);
    showAddFriendDialog.value = false;
    newFriendId.value = "";
    console.log(`Added new friend: ${friendId}`);
    await getRecentFriends();
  } else {
    console.error("Friend ID cannot be empty");
  }
};

// 获取好友列表
const getRecentFriends = async () => {
  const { data } = await recentList();
  friends.value = data;
};

const scrollToTop = ref(-1);

// 是否可以加载更多消息
const canLoadMore = computed(() => {
  if (messages.value.length < 10) {
    return false;
  }
  if (scrollToTop.value !== 0) {
    // 没有到顶
    return false;
  }
  if (loading.value) {
    // 正在加载中
    return false;
  }
  return !noMoreMessages.value;
});

// 监听是否滚动到顶部
watch(
  () => canLoadMore.value,
  async canLoadMore => {
    if (canLoadMore) {
      await handleLoadMoreMessages();
    }
  }
);

const handleScroll = () => {
  scrollToTop.value = chatBody.value.scrollTop;
};

const handleLoadMoreMessages = async () => {
  if (noMoreMessages.value || loading.value) return;
  loading.value = true;
  await loadMoreMessages();
  loading.value = false;
};

const loadMoreMessages = async () => {
  if (!activeFriend.value) {
    console.error("friend is required");
    return;
  }

  const chatBodyElement = chatBody.value;

  // 记录当前可见的第一个消息的 ID 和距离顶部的偏移量
  const firstVisibleMessage = chatBodyElement.querySelector(".message");
  if (!firstVisibleMessage) return;

  const firstVisibleMessageId = firstVisibleMessage.getAttribute("data-id");
  const offsetFromTop = firstVisibleMessage.getBoundingClientRect().top;

  const lastMessageId = messages.value[0].messageId;
  if (!lastMessageId) {
    console.log("last message is null");
    return;
  }

  const { data } = await loadMessages({
    targetUid: activeFriend.value.memberId,
    lastMessageId: lastMessageId
  });

  const { records } = data;
  if (records.length === 0) {
    noMoreMessages.value = true;
    return;
  }
  const reversedRecords = records.reverse();
  activeFriend.value.last10Messages = [...reversedRecords, ...messages.value];

  await nextTick(() => {
    // 等待消息渲染完成后，重新定位到原来的第一条可见消息
    const newFirstVisibleMessage = chatBodyElement.querySelector(
      `[data-id='${firstVisibleMessageId}']`
    );
    if (newFirstVisibleMessage) {
      const newOffset = newFirstVisibleMessage.getBoundingClientRect().top;
      chatBodyElement.scrollTop += newOffset - offsetFromTop;
    }
  });
};

// ===================== 通话部分 ======================

const callerInfo = ref({});

/**
 * 收到通话邀请
 */
const onCall = () => {
  wsClient.registerActionHandler("webrtc.offer", message => {
    console.log("webrtc.call", message);
    const { data } = message;
    const { fromUid } = data;
    callerInfo.value = data;
    // 开启新的窗口，显示视频接听界面
    ipcRenderer.invoke("open-win", {
      path: "call",
      width: 640,
      height: 480,
      params: {
        isCaller: false,
        fromUser: fromUid,
        toUser: useUserStoreHook().username,
        message: JSON.stringify(message)
      }
    });
  });
};

// 发起视频通话
const handleStartVideoCall = async () => {
  console.log("start video call");
  const friend = activeFriend.value;
  if (!friend) {
    console.error("friend is required");
    return;
  }
  // 弹窗展示自己的视频
  // 开启新的窗口，显示视频接听界面
  await ipcRenderer.invoke("open-win", {
    path: "call",
    width: 640,
    height: 480,
    params: {
      fromUser: useUserStoreHook().username,
      toUser: friend.memberId,
      isCaller: true
    }
  });
};

onMounted(() => {
  onMessageArrived();
  onMessageRead();
  onCall();
  getRecentFriends();
});
</script>

<style scoped lang="scss">
.chat-app {
  display: flex;
  height: 100vh;
  font-family: "Helvetica Neue", Arial, sans-serif;
  background-color: #f0f2f5;

  .chat-section {
    display: flex;
    flex-direction: column;
    width: 30vw;
    background-color: #fff;

    .default-page {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100%;
      color: #888;
      text-align: center;
    }

    .chat-header {
      position: relative;
      height: 76px;
      padding: 20px;
      font-size: 1.5em;
      font-weight: bold;
      color: #fff;
      text-align: center;
      background: rgb(255 255 255 / 100%);
      box-shadow: 0 4px 15px rgb(168 168 168 / 15%);
      opacity: 1;

      .avatar {
        position: absolute;
        left: 20px;
        width: 40px;
        height: 40px;
        border-radius: 50%;
      }

      .nickname {
        position: absolute;
        top: 16px;
        left: 70px;
        font-size: 16px;
        font-weight: 400;
        color: rgb(28 28 28 / 100%);
      }

      .status {
        position: absolute;
        top: 40px;
        left: 70px;
        font-size: 11px;
        font-weight: 400;
        color: rgb(28 28 28 / 100%);

        .point {
          display: inline-block;
          width: 8px;
          height: 8px;
          margin-right: 5px;
          background-color: #4cd964;
          border-radius: 50%;
        }
      }

      .call {
        position: absolute;
        top: 20px;
        right: 20px;
        width: 21px;
        height: 21px;
      }
    }

    .chat-body {
      flex: 1;
      padding: 20px;
      overflow-y: auto;
      background-color: #f9f9f9;

      /** 不展示滚动条 */
      &::-webkit-scrollbar {
        display: none;
      }

      .no-more {
        margin-bottom: 18px;
        font-size: 10px;
        color: #888;
        text-align: center;
      }

      .load-more {
        margin-bottom: 18px;
        font-size: 10px;
        color: #888;
        text-align: center;
      }

      .loading {
        margin-bottom: 18px;
        font-size: 10px;
        color: #888;
        text-align: center;
      }

      .message {
        margin-bottom: 10px;

        .message-timestamp {
          margin-top: 5px;
          font-size: 12px;
          color: #888;
        }

        .message-text {
          position: relative;
          max-width: 60%;
          padding: 10px;
          font-size: 14px;
          line-height: 1.5;
          word-wrap: break-word;
          border-radius: 18px 10px 18px 0;
          box-shadow: 0 1px 1px rgb(0 0 0 / 10%);

          .unread-dot {
            position: absolute;
            top: -5px;
            right: -5px;
            width: 8px;
            height: 8px;
            background-color: red;
            border-radius: 50%;
          }

          &.me {
            align-self: flex-end;
            background-color: #edf4fd;
          }

          &.friend {
            align-self: flex-start;
            background-color: #fff;
          }
        }
      }
    }
  }
}
</style>
